Add streaming ORDER BY queries#4800
Conversation
Implement resumable cross-partition ORDER BY merging with exact Cosmos value ordering, durable continuation state, and split-safe recovery. Add cross-SDK scenario coverage, emulator validation, and live split tests.\n\nFixes Azure#4756\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 80371d09-63a0-4ebc-8bfc-6af45c217151
Replace the draft pull request placeholders with the upstream PR number.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 80371d09-63a0-4ebc-8bfc-6af45c217151
Resolve planner and Cosmos status-code conflicts while preserving upstream query-range normalization and streaming ORDER BY support.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 80371d09-63a0-4ebc-8bfc-6af45c217151
analogrelay
left a comment
There was a problem hiding this comment.
Some initial comments. Looking like a good start though! I still need to go through the actual merge processes so not ready to approve quite yet.
| Future node types (e.g., streaming ORDER BY, hybrid search) may require partial parsing of item bodies. The backend query plan can rewrite the query to use a standardized envelope (promoting ordering keys to top-level fields and demoting the raw user document to a `payload` field). This varied-shape pattern must be considered in the overall design direction, but does not need to be accommodated in the current implementation. | ||
| `StreamingOrderedMerge` (cross-partition `ORDER BY`) is the first node type that parses item bodies: the backend query plan's `rewrittenQuery` reshapes each per-partition result into a standardized envelope (`{"_rid": ..., "orderByItems": [...], "payload": ...}` — sort keys promoted to top-level fields, the raw user document demoted to `payload`). The pipeline parses just enough of this envelope to compare rows and emit `payload` — see `driver::dataflow::order_by` (the value/comparator/resume-value model) and `driver::dataflow::query_response` (envelope parsing and response reassembly). Hybrid search, if implemented, would need the same pattern for its own envelope shape. | ||
|
|
||
| Resume for streaming `ORDER BY` is per-range and topology-change-safe. Each still-active range persists its last-emitted key tuple and envelope `_rid`. On resume — including after a partition split or merge — a **scalar** boundary is turned into a key-only seek filter (`... > boundary OR ... = boundary`) that returns every row at or after the boundary, including the full-key tie run; a `_rid` predicate is deliberately not part of this SQL, since `_rid` compares as an ordinal base64 string in Cosmos SQL, not the backend's numeric document order. The already-emitted prefix of that tie run is instead discarded client-side by a numeric `_rid` comparison (see `models::resource_id::compare_document_rids`), so resume stays exact per row and splits/clips to sub-ranges without any positional row count. An **array/object** (complex) sort key is persisted only as a bounded 128-bit hash, so it cannot form a seek filter; it resumes by a whole-range positional re-scan, which is valid only while the range's topology is unchanged — a resume that would cross a split is rejected with a typed error (`CosmosStatus::CLIENT_STREAMING_MERGE_COMPLEX_BOUNDARY_TOPOLOGY_CHANGE`) rather than risk silently dropping rows. |
There was a problem hiding this comment.
An array/object (complex) sort key is persisted only as a bounded 128-bit hash, so it cannot form a seek filter;
Is that what other SDKs do? Just want to make sure we're not introducing an arbitrary limitation here that other SDKs don't have. In general, I think array/object sort keys are an anti-pattern anyway, so I'm not really that worried.
There was a problem hiding this comment.
.NET also stores array/object resume values as a bounded 128-bit low/high DistinctHash and sends them through the structured resumeFilter understood by the backend. Java explicitly does not support continuation tokens for array/object ORDER BY keys. I switched Rust to the .NET resumeFilter shape and ported the backend/.NET DistinctHash exactly, with the published .NET baseline vectors as tests. The spec now calls out the peer behavior.
There was a problem hiding this comment.
Although in java this is something we do not support.
Align resume handling with the .NET structured resume filter and DistinctHash, add JOIN skip-count support, preserve split-node abstraction, and update emulator coverage for the now-supported query path.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 80371d09-63a0-4ebc-8bfc-6af45c217151
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
/azp run rust - cosmos - weekly |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Adds resumable, cross-partition streaming ORDER BY execution to the Cosmos driver, including deterministic ordering and partition-split recovery.
Changes:
- Implements ordered merging, continuation snapshots, resume filtering, and RID tie-breaking.
- Adds emulator query rewriting and complex-value hashing support.
- Adds scenario, integration, emulator, and live split coverage.
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
sdk/cosmos/.cspell.json |
Adds new technical terms. |
sdk/cosmos/azure_data_cosmos/CHANGELOG.md |
Announces ORDER BY support. |
sdk/cosmos/azure_data_cosmos/tests/emulator_tests/cosmos_query.rs |
Updates query expectations. |
sdk/cosmos/azure_data_cosmos/tests/in_memory_emulator.rs |
Registers emulator coverage. |
sdk/cosmos/azure_data_cosmos/tests/split_tests/cosmos_query_order_by_split.rs |
Adds live split-resume tests. |
sdk/cosmos/azure_data_cosmos/tests/split_tests/mod.rs |
Registers split tests. |
sdk/cosmos/azure_data_cosmos_driver/CHANGELOG.md |
Documents driver behavior. |
sdk/cosmos/azure_data_cosmos_driver/Cargo.toml |
Updates test dependencies. |
sdk/cosmos/azure_data_cosmos_driver/docs/FEED_OPERATIONS_REQS.md |
Updates query requirements. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/cosmos_driver.rs |
Advertises ORDER BY capabilities. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/distinct_hash.rs |
Implements Cosmos-compatible hashing. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/integration_tests/mod.rs |
Registers integration tests. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/integration_tests/order_by_resume.rs |
Tests resume behavior. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/mocks.rs |
Extends dataflow test mocks. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/mod.rs |
Registers ORDER BY modules. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/order_by.rs |
Implements ordering and boundaries. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/planner.rs |
Builds ordered pipelines. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/query_response.rs |
Processes rewritten envelopes. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/snapshot.rs |
Adds durable ordered state. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/streaming_ordered_merge.rs |
Implements streaming merge and repair. |
sdk/cosmos/azure_data_cosmos_driver/src/error/cosmos_status.rs |
Extends status handling. |
sdk/cosmos/azure_data_cosmos_driver/src/in_memory_emulator/operations.rs |
Synthesizes ORDER BY plans. |
sdk/cosmos/azure_data_cosmos_driver/src/in_memory_emulator/rid.rs |
Supports deterministic emulator RIDs. |
sdk/cosmos/azure_data_cosmos_driver/src/models/mod.rs |
Registers model support. |
sdk/cosmos/azure_data_cosmos_driver/src/models/resource_id.rs |
Parses and compares resource IDs. |
sdk/cosmos/azure_data_cosmos_driver/src/query/eval/mod.rs |
Evaluates rewritten queries. |
sdk/cosmos/azure_data_cosmos_driver/src/query/mod.rs |
Exposes query internals. |
sdk/cosmos/azure_data_cosmos_driver/tests/fixtures/streaming_order_by_scenarios.json |
Catalogs cross-SDK scenarios. |
sdk/cosmos/azure_data_cosmos_driver/tests/in_memory_emulator_tests/mod.rs |
Registers ORDER BY tests. |
sdk/cosmos/azure_data_cosmos_driver/tests/in_memory_emulator_tests/order_by.rs |
Tests emulator ordering. |
sdk/cosmos/azure_data_cosmos_driver/tests/streaming_order_by_scenario_catalog.rs |
Validates scenario coverage. |
Comments suppressed due to low confidence (2)
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/order_by.rs:345
- Objects have the same Cosmos ordering rule as arrays: their DistinctHash values are compared with the UInt128 binary comparer. Sorting keys and values structurally produces a different order from the service, so a cross-partition merge can emit object-keyed rows out of order. Reuse the same complex-hash comparator for this branch.
(Self::Object(a), Self::Object(b)) => {
let mut a_sorted: Vec<&(String, OrderByItem)> = a.iter().collect();
let mut b_sorted: Vec<&(String, OrderByItem)> = b.iter().collect();
a_sorted.sort_by(|x, y| x.0.cmp(&y.0));
b_sorted.sort_by(|x, y| x.0.cmp(&y.0));
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/order_by.rs:682
- The resume-boundary string comparison must use the same ordinal UTF-16 relation as the merge comparator and Cosmos. Using Rust's scalar/UTF-8 order here can misclassify non-BMP boundaries after a split, causing rows to be skipped or replayed.
(OrderByItem::String(a), OrderByResumeValue::String { value }) => {
ColumnCmp::Ordered(a.as_str().cmp(value.as_str()))
}
Summary
ORDER BYexecutionTesting
cargo test -p azure_data_cosmos_driver --all-featurescargo clippy -p azure_data_cosmos_driver --all-features --all-targets -- -D warningscargo clippy -p azure_data_cosmos --all-features --all-targets -- -D warningsFixes #4756